package ie.lawlor.amvc.event;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import foxtrot.Task;
import foxtrot.Worker;

/**
 * @author blawlor
 */
public class ThreadedReflectionEventHandler extends ReflectionEventHandler {

	/** Exception thrown during worker thread. */    
	protected Throwable workerException;
	/** Message string generated during worker thread. */    
	protected String workerMessage;
	
    public ThreadedReflectionEventHandler() {
    }

    public ThreadedReflectionEventHandler(
        IEventSink target,
        String loggerName) {
        super(target, loggerName);
    }

    public void handleEvent(Event event) throws EventNotHandledException {
    try {
      logger.debug("Trying to use simple (non-threaded) event handler");
      super.handleEvent(event);
    } catch (EventNotHandledException ex) {
      logger.debug("Simple (non-threaded) event handler could not handle the event. " +
          "Trying multi-threaded event handler");
      try {
      callSimpleMethod("fireCursorWaiting", target);
      } catch (Throwable ignore){
        ignore.printStackTrace();        
      }
      try {
        Worker.post(new EventTask(event));
      } catch (EventNotHandledException forward) {
        throw forward;
      } catch (Throwable ignore) {
        ignore.printStackTrace();
      } finally{
        try {
        callSimpleMethod("fireCursorNormal", target);
        } catch (Throwable ignore){
          ignore.printStackTrace();
        }
      }
    }
  }
    
    private void callSimpleMethod(String name, Object target) throws Throwable{
      try {
        Class c = target.getClass();
        Method doMethod = c.getMethod(name, new Class[]{});
        Object result = doMethod.invoke(target, new Object[]{});
      } catch (NoSuchMethodException e1) {
        throw new EventNotHandledException(e1);
      } catch (SecurityException e1) {
        logger.error("Couldn't call event handler (SecurityException)", e1);
      } catch (IllegalArgumentException e1) {
        logger.error("Couldn't call event handler (IllegalArgumentException)", e1);
      } catch (IllegalAccessException e1) {
        logger.error("Couldn't call event handler (IllegalAccessException)", e1);
      } catch (InvocationTargetException e1) {
        throw e1.getTargetException();
      }      
    }
    
	private void callMethod(Event e, String suffix, Class[] argTypes, Object[] args) throws EventNotHandledException, Throwable {
		logger.debug("Entering: Handling " + e);
		Class c = target.getClass();
    String methodName = "do" + e.getName() + suffix;
		try {
			Method doMethod = c.getMethod(methodName, argTypes);
			Object result = doMethod.invoke(target, args);
		} catch (NoSuchMethodException e1) {
			throw new EventNotHandledException(e1);
		} catch (SecurityException e1) {
			logger.error("Couldn't call event handler (SecurityException)", e1);
		} catch (IllegalArgumentException e1) {
			logger.error("Couldn't call event handler (IllegalArgumentException)", e1);
		} catch (IllegalAccessException e1) {
			logger.error("Couldn't call event handler (IllegalAccessException)", e1);
		} catch (InvocationTargetException e1) {
			throw e1.getTargetException();
		}
	}
    private void callMethod(Event e, String suffix, Object[] args) throws EventNotHandledException, Throwable {
		Class[] argTypes = new Class[args.length];
		for (int i = 0; i< args.length; ++i){
			argTypes[i] = args[i].getClass();
		}
    	callMethod(e, suffix, argTypes, args);
    }
    
    protected class EventTask extends Task {
    	private Event e;
    	
    	public EventTask(Event e){
    		this.e = e;
    	}
    	
		public Object run() throws Exception{
			Object[] args = new Object[]{e};
			try {
				callMethod(e, "Thread", args);
				callMethod(e, "Finished", args);
			} catch(EventNotHandledException enh){
				logger.warn("Event " + e + " is not being handled by class " + target.getClass());				
			} catch(Throwable t){
				try {
					Class[] argTypes = new Class[]{Throwable.class};
					args = new Object[]{t};
					callMethod(e, "Exception", argTypes, args);
				} catch(EventNotHandledException enh){
					logger.warn("Exception to Event " + e + " is not being handled by class " + target.getClass());				
				} catch (Throwable t1){
					logger.error(t1);
				}
			} finally{
				try {
					callMethod(e, "Finally", args);
				} catch(EventNotHandledException enh){
					logger.warn("Finally clause for Event " + e + " is not being handled by class " + target.getClass());				
				} catch (Throwable t){
					logger.error(t);					
				}
			}
			return null;
		}    	
    }
}
